/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package controlleur.reponseQuestionnaire;

import controlleur.QuestionnaireController;
import controlleur.UserAuthController;
import entities.Proposition;

import entities.question.AbstractQuestion;
import entities.question.QuestionChoix;
import entities.question.QuestionOuverte;
import entities.question.QuestionUnique;
import entities.questionnaire.Questionnaire;
import entities.reponse.AbstractReponse;
import entities.reponse.ReponseChoix;
import entities.reponse.ReponseLibre;
import entities.utilisateur.AbstractRepondant;
import entities.utilisateur.RepondantAuthentifie;
import facade.QuestionnaireFacadeLocal;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.SessionScoped;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.faces.model.SelectItem;

/**
 *
 * @author aurelien.emery
 */
@ManagedBean(name = "reponseQuestionnaireController")
@SessionScoped
public class ReponseQuestionnaireController implements Serializable {

    /**
     * partie commune du chemin vers lequel se trouve les fichiers principaux
     */
    private String repertoireQuest = "/pages/includes/";
    //TODO remplacer par une facade
    @EJB
    private QuestionnaireFacadeLocal daoQuestionnaire;
    /**
     * controller de navigation dans le questionnaire.
     * Celui-ci gere le type de bandeau a afficher, ainsi que les methodes d appel des differents boutons (ou chrono)
     */
    @ManagedProperty(value = "#{navigationQuestionController}")
    private NavigationQuestionController navigationQuestionController;
    /**
     * controller de connexion au site. permet de verifier si l utilisateur est authentifié
     */
    @ManagedProperty(value = "#{userAuthController}")
    private UserAuthController userAuthController;
    /**
     * controller des questionnaires : permet de faire le lien avec le tableau des questionnaires ou le lien de passage de test
     *
     */
    @ManagedProperty(value = "#{questionnaireController}")
    private QuestionnaireController questionnaireController;

    private int indiceQuestionCourante;
    // attribut indiquant le type de fichier/question : -2 page cloture; -1 page accueil; 0 ouvert; 1 unique; 2 multiple
    private int typeQuestion;
    // sert a masquer/afficher la question ou le bouton de suite pour le chrono
    private boolean questionVisible;
    private Questionnaire questionnaire;
    private AbstractRepondant repondant;
    // reponse pour question libre ou choix unique
    private String reponse;
    // reponse pour choix multiple
    private List<String> reponseChoix;
    // map des reponses donnees par l utilisateur
    private Map<Long, AbstractReponse> reponseDonnee;
    // cet attribut definit si le questionnaire est en test (pas de persistence) ou en production
    private boolean test;

    /**
     * constructeur par defaut
     */
    public ReponseQuestionnaireController() {
        initVar();
        reponse = "";
        reponseChoix = new ArrayList();
    }

    private void initVar() {
        indiceQuestionCourante = -1;
        typeQuestion = 0;
        repondant = null;

        reponse = null;
        questionnaire = null;
        reponseDonnee = new HashMap<Long, AbstractReponse>();
        test = false;
    }

    /**
     * Methode permettant d initialiser le controller, apres construction de celui-ci.
     * Cette methode permet de faire appel aux differents attributs gérés par les EJB, avant la construction de la page.
     */
    @PostConstruct
    public void init() {
        initVar();
        // initialisation du bean de navigation en cas de retour sur questionnaire
        navigationQuestionController.init();
        //gestion des parametres envoyés dans la requete
        if (userAuthController.getIsConnecte()) {


            questionnaire = questionnaireController.getSelected();
            System.out.println("questionnaire : "+questionnaire);

            test = questionnaireController.getIsTest();
            if (test){
                // creation d un repondant fictif
            repondant = new RepondantAuthentifie();
            }else{
                repondant = daoQuestionnaire.findRepondant(userAuthController.getLogin());
            }
        } else {
            Map<String, String> requestParam = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap();
            String user = requestParam.get("u");
            String quest = requestParam.get("q");
            String ctrl = requestParam.get("key");
            questionnaire = daoQuestionnaire.findQuestionnaire(user, quest, ctrl);
            repondant = questionnaire.getListCible().get(0);
        }

        // necessaire pour l affichage de la page de questionnaire introuvable
        if (questionnaire == null) {
            indiceQuestionCourante = -3;
        }
        navigationQuestionController.setConfiguration(null, this);
        typeQuestion();
        questionVisible = true;
    }

    /**
     * Cette methode permet de debuter le questionnaire
     */
    public void demarrer() {
        // positionnement sur la 1ere question
        indiceQuestionCourante = 0;
        // configuration du controller de navigation
        navigationQuestionController.setConfiguration(questionnaire.getTypeScenario(), this);
        typeQuestion();
    }

    /**
     * Cette methode permet de terminer un questionnaire. Elle affiche la page de cloture et valide la derniere reponse
     */
    public String terminer() {
        System.out.println("reponseQuestionnaire.terminer");
        validerReponse();
        if (test) {
            // reinitialisation du bean, en cas de passage d un nouveau questionnaire en test
            initVar();
            return "ToQuestionnaireLister";
        } else {
            // lien vers la page de cloture
            indiceQuestionCourante = -2;

            // configuration du controller de navigation, pour afficher le bandeau vide
            navigationQuestionController.setConfiguration(null, this);
            typeQuestion();
            daoQuestionnaire.envoiMail(repondant, questionnaire);
            return null;
        }
    }

    /**
     * Cette methode permet de changer la question courante pour la suivante ou la precedente
     * elle sert aussi a enregistrer la reponse
     * @param questionSuivante 1 pour la question suivante, -1 pour la question precedente
     */
    public void changeQuestion(int questionSuivante) {
        // validation de la question en cours
        if (getNbQuestion() == getNumQuestion()) {
            terminer();
        } else {
            validerReponse();
            if (questionSuivante == 1 || questionSuivante == -1) {
                indiceQuestionCourante += questionSuivante;
                // mise a jour des attributs reponse et reponseChoix
                reponse = "";

                reponseChoix.clear();
                AbstractReponse rep = reponseDonnee.get(getQuestionCourante().getId());
                if (rep != null) {
                    if (ReponseChoix.class.isAssignableFrom(rep.getClass())) {
                        if (((ReponseChoix) rep).getListPropositionChoisies().size() > 0) {
                            if (QuestionUnique.class.isAssignableFrom(rep.getQuestion().getClass())) {
                                reponse = String.valueOf(getQuestionCourante().getPropositions().indexOf(((ReponseChoix) rep).getListPropositionChoisies().get(0)));
                            } else {
                                for (Proposition p : ((ReponseChoix) rep).getListPropositionChoisies()) {
                                    reponseChoix.add(String.valueOf(getQuestionCourante().getPropositions().indexOf(p)));
                                }
                            }
                        }
                    } else {
                        reponse = ((ReponseLibre) rep).getReponse();
                    }
                } else {
                    // mise en place de valeur par defaut des questions a choix unique (radio button)
                    if (QuestionUnique.class.isAssignableFrom(getQuestionCourante().getClass())) {
                        reponse = "0";
                    }
                }
                typeQuestion();
            }
        }
    }

    /**
     * Cette methode permet de valider la reponse d une question chronometre quand le temps est arrivé au bout du decompte
     * @param ae
     */
    public String suite() {
        String result = null;
        questionVisible = true;

        // redeclenchement du chrono
        navigationQuestionController.suivant(null);

        return result;

    }

    /**
     * Renvoit la question courante du questionnaire, ou la 1ere question si la page a afficher est l accueil ou la cloture.
     * L envoi de la 1ere question par defaut evite de lever une exception dans la page, avec le rechargement automatique de JSF.
     * @return
     */
    public AbstractQuestion getQuestionCourante() {
        // ce test permet de pallier au rechargement automatique de JSF,
        // lors de la cloture du questionnaire, pour l indice -1 et -2 qui amene la page d accueil et de cloture
        if (indiceQuestionCourante >= 0) {
            return questionnaire.getListQuestion().get(indiceQuestionCourante);
        }
        return questionnaire.getListQuestion().get(0);
    }

    /**
     * Renvoit la chaine definissant le fichier principal a afficher, ainsi que son emplacement
     * @return
     */
    public String getQuestion() {
        // controle pour recharger les valeurs initiales en cas de retour sur un questionnaire (cas des tests ou enchainement de questionnaires a passer
        if (questionnaire == null) {
            init();
        }
        return repertoireQuest + nomQuest();
    }

    /**
     * Methode privé permettant de gerer l affichage de la partie principale de la page, ainsi que le fonctionnement des elements associés
     */
    private void typeQuestion() {
        switch (indiceQuestionCourante) {
            case -3: {
                // cette valeur indique qu il n y a pas de questionnaire a passer
                typeQuestion = -3;
                break;
            }
            case -2: {
                // permet d acceder a la page de cloture
                typeQuestion = -2;
                break;
            }
            case -1: {
                // permet d acceder a la page d accueil
                typeQuestion = -1;
                break;
            }
            default: {
                if (getQuestionCourante().getClass().equals(QuestionChoix.class)) {
                    typeQuestion = 2;
                } else if (getQuestionCourante().getClass().equals(QuestionOuverte.class)) {
                    typeQuestion = 0;
                } else {

                    // question choix unique
                    typeQuestion = 1;
                }
            }
        }
    }

    /**
     * Cette methode definit le fichier principal a afficher.
     * Celui-ci peut etre la page d accueil, la page de cloture, la page d erreur ou une page de question
     * @return la page priincipal a afficher
     */
    private String nomQuest() {
        switch (typeQuestion) {
            case -3: {
                return "questionnaire/introuvable.xhtml";
            }
            case -2: {
                return "questionnaire/cloture.xhtml";
            }
            case -1: {
                return "questionnaire/accueil.xhtml";
            }
            case 0: {
                return "question/questionLibre.xhtml";
            }
            case 1: {
                return "question/questionUnique.xhtml";
            }
            case 2: {
                return "question/questionMultiple.xhtml";
            }
            default: {
                return "question/questionLibre.xhtml";
            }
        }
    }

    /**
     * Permet de valider une reponse.
     * Suivant la valeur de l attribut test, les reponses sont persistées ou non.
     * La Map<long,Reponse> reponseDonnee est une image des reponses donnees.
     */
    private void validerReponse() {
        if (test) {
            // methode de validation des reponses en test
            validerReponseTest();
        } else {
            // methode de validation des repones en production
            validerReponseProduction();
        }
    }

    /**
     * Permet de valider la reponse en cours.
     * Cette methode enregistre la reponse si elle n existe pas, ou la met a jour.
     * La Map<long,Reponse> reponseDonnee est une image des reponses donnees enregistrées en base.
     * Cet attribut permet de limiter les acces base en conservant les reponses au niveau du managedBean
     */
    private void validerReponseProduction() {
        System.out.println("reponse : " + reponse);
        System.out.println("type question : " + typeQuestion);
        switch (typeQuestion) {
            // reponse ouverte
            case 0: {
                reponseDonnee.put(getQuestionCourante().getId(),
                        daoQuestionnaire.addReponseLibre((ReponseLibre) reponseDonnee.get(getQuestionCourante().getId()), reponse, questionnaire, getQuestionCourante()));
                break;
            }

            // reponse choix unique
            case 1: {
                List<Proposition> rep = new ArrayList<Proposition>();
                rep.add(getQuestionCourante().getPropositions().get(new Integer(reponse)));

                reponseDonnee.put(getQuestionCourante().getId(),
                        daoQuestionnaire.addReponseChoix((ReponseChoix) reponseDonnee.get(getQuestionCourante().getId()), rep, questionnaire, getQuestionCourante()));
                break;
            }
            // reponse choix multiple (meme type de composant que choix unique)
            case 2: {
                // recuperation des propositions depuis les indices selectionnes
                List<Proposition> rep = new ArrayList<Proposition>();
                for (String i : reponseChoix) {
                    rep.add(getQuestionCourante().getPropositions().get(new Integer(i)));
                }
                reponseDonnee.put(getQuestionCourante().getId(),
                        daoQuestionnaire.addReponseChoix((ReponseChoix) reponseDonnee.get(getQuestionCourante().getId()), rep, questionnaire, getQuestionCourante()));
                break;
            }
            // normalement inutile
            default: {
            }
        }
    }

    /**
     * Permet de valider la reponse en cours.
     * Aucun enregistrement en base n est effectué, car il s agit de test du questionnaire
     * La Map<long,Reponse> reponseDonnee est une image des reponses donnees.
     *
     */
    private void validerReponseTest() {
        switch (typeQuestion) {
            // reponse ouverte
            case 0: {
                ReponseLibre r = new ReponseLibre(getQuestionCourante(), repondant, reponse);
                r.setQuestionnaire(questionnaire);
                reponseDonnee.put(getQuestionCourante().getId(), r);
                break;
            }
            // reponse choix unique
            case 1: {
                List<Proposition> rep = new ArrayList<Proposition>();
                rep.add(getQuestionCourante().getPropositions().get(new Integer(reponse)));
                ReponseChoix r = new ReponseChoix(getQuestionCourante(), repondant, rep);
                r.setQuestionnaire(questionnaire);
                reponseDonnee.put(getQuestionCourante().getId(), r);
                break;
            }
            // reponse choix multiple (meme type de composant que choix unique)
            case 2: {
                // recuperation des propositions depuis les indices selectionnes
                List<Proposition> rep = new ArrayList<Proposition>();
                for (String i : reponseChoix) {
                    rep.add(getQuestionCourante().getPropositions().get(new Integer(i)));
                }
                ReponseChoix r = new ReponseChoix(getQuestionCourante(), repondant, rep);
                r.setQuestionnaire(questionnaire);
                reponseDonnee.put(getQuestionCourante().getId(), r);
                break;
            }
            // normalement inutile
            default: {
            }
        }
    }

    /**
     * Methode definissant le contenu des liste de radiobouton ou checkbox
     * La valeur de chaque element correspond a l indice dans la propriete getPropositions de la question courante
     * @return
     */
    public SelectItem[] getPropositions() {
        try {
            SelectItem[] result = new SelectItem[getQuestionCourante().getPropositions().size()];
            for (int i = 0; i < getQuestionCourante().getPropositions().size(); i++) {
                result[i] = new SelectItem(i, getQuestionCourante().getPropositions().get(i).getTexte());
            }
            return result;
        } catch (UnsupportedOperationException ex) {
            // ce cas n est pas a traiter, il sert a pallier le chargement auto des composants existants.
            // Ce pb se produit quand on passe d une question a choix multiple ou unique vers une question ouverte
            return null;
        }
    }

    /**
     * Reponse dans le cas d'une question ouverte ou unique
     * @return
     */
    public String getReponse() {
        return reponse;
    }

    public void setReponse(String reponseLibre) {
        this.reponse = reponseLibre;
    }

    /**
     * Reponse dans le cas d une question a choix multiple
     * @return
     */
    public List<String> getReponseChoix() {
        return reponseChoix;
    }

    public void setReponseChoix(List<String> reponseChoix) {
        this.reponseChoix = reponseChoix;
    }

    public boolean isQuestionVisible() {
        return questionVisible;
    }

    public void setQuestionVisible(boolean questionVisible) {
        this.questionVisible = questionVisible;
    }

    public int getNbQuestion() {
        return questionnaire.getListQuestion().size();
    }

    public int getNumQuestion() {
        return indiceQuestionCourante + 1;
    }

    public void setNumQuestion(int i) {
    }

    public Questionnaire getQuestionnaire() {
        return questionnaire;
    }

    public void setQuestionnaire(Questionnaire questionnaire) {
        this.questionnaire = questionnaire;
    }

    public NavigationQuestionController getNavigationQuestionController() {
        return navigationQuestionController;
    }

    public void setNavigationQuestionController(NavigationQuestionController navigationQuestionController) {
        this.navigationQuestionController = navigationQuestionController;
    }

    public QuestionnaireController getQuestionnaireController() {
        return questionnaireController;
    }

    public void setQuestionnaireController(QuestionnaireController questionnaireController) {
        this.questionnaireController = questionnaireController;
    }

    public UserAuthController getUserAuthController() {
        return userAuthController;
    }

    public void setUserAuthController(UserAuthController userAuthController) {
        this.userAuthController = userAuthController;
    }
}
